"""
@Author：十
@Time：2024/12/30 10:50
@FileName：SerialThread.py
@Description：串口线程
"""

import logging
import time

import serial
import serial.tools.list_ports
from PyQt5.QtCore import QThread, pyqtSignal, QMutex, QMutexLocker, QWaitCondition
from PyQt5.QtCore import QTimer
from loguru import logger
from serial import SerialException
from serial.serialutil import SerialTimeoutException

# 配置详细的日志记录
logger = logging.getLogger('SerialThread')
logger.setLevel(logging.DEBUG)

# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 创建格式化器
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)


class SerialThread(QThread):
    # 定义信号
    data_received = pyqtSignal(bytes)
    send_complete = pyqtSignal(bool, bytes)
    serial_status = pyqtSignal(str)  # 串口状态信号
    serial_error = pyqtSignal(str)  # 错误信号
    serial_success = pyqtSignal(bool)

    modbus_read_device = pyqtSignal(bytes)
    modbus_read_temperature = pyqtSignal(bytes)  # 读保持寄存器（地址0x05DD）

    # MODBUS特定信号 - 根据功能码和寄存器地址区分
    modbus_read_coils_response = pyqtSignal(bytes)  # 读线圈
    modbus_read_discrete_inputs_response = pyqtSignal(bytes)  # 读离散输入
    modbus_write_single_coil_response = pyqtSignal(bytes)  # 写单个线圈
    modbus_write_multiple_coils_response = pyqtSignal(bytes)  # 写多个线圈
    modbus_write_multiple_registers_response = pyqtSignal(bytes)  # 写多个寄存器
    modbus_response_timeout = pyqtSignal(int, int)  # 超时信号 (function_code, address)
    # MODBUS特定信号 - 根据功能码和寄存器地址区分
    modbus_read_holding_registers_response = pyqtSignal(bytes)  # 读保持寄存器（通用）
    modbus_read_holding_registers_0100_response = pyqtSignal(bytes)  # 读保持寄存器（地址0x0100）

    modbus_read_input_registers_response = pyqtSignal(bytes)  # 读输入寄存器（通用）
    modbus_read_input_registers_0200_response = pyqtSignal(bytes)  # 读输入寄存器（地址0x0200）

    modbus_write_single_register_response = pyqtSignal(bytes)  # 写单个寄存器（通用）
    modbus_write_single_register_0100_response = pyqtSignal(bytes)  # 写单个寄存器（地址0x0100）

    def __init__(self, port, baudrate, parent=None):
        super().__init__(parent)
        self.port = port
        self.baudrate = baudrate
        self.running = False  # 线程运行标志
        self.ser = None  # 串口对象
        self._buffer = bytearray()  # 接收缓冲区
        self._max_buffer_size = 10 * 1024 * 1024  # 最大缓冲区10MB

        # 线程同步机制
        self._lock = QMutex()
        self._condition = QWaitCondition()
        self._send_queue = []  # 发送队列 (格式: (priority, data, expected_response_addr))
        self._reconnect_attempts = 0
        self._max_reconnect = 5

        # MODBUS相关
        self._pending_requests = {}  # 存储待处理的请求 {transaction_id: (function_code, address)}
        self._response_timer = None  # 响应超时定时器
        self._current_expected_response = None  # 当前期望的响应信息

    def run(self):
        self.running = True
        self.serial_status.emit("正在启动...")

        while self.running:
            try:
                # 确保串口连接
                if not self._ensure_serial_connection():
                    time.sleep(1)  # 连接失败后等待
                    continue

                # 主处理循环
                self._process_send_queue()
                self._process_receive_data()

                # 无工作时短暂休眠
                self._idle_sleep()

            except Exception as e:
                logger.critical(f"串口线程主循环异常: {e}")
                self.serial_error.emit(f"主循环异常: {str(e)}")
                self._close_serial()
                time.sleep(0.5)

    def _ensure_serial_connection(self):
        if self.ser and self.ser.is_open:
            return True

        try:
            self.serial_status.emit("正在连接...")

            self.ser = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                timeout=0.1,  # 读超时时间
                write_timeout=0.5,  # 写超时时间
                bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_ONE
            )

            if self.ser.is_open:
                self._reconnect_attempts = 0
                self.serial_status.emit("已连接")
                self.serial_success.emit(True)
                return True
        except SerialException as se:
            self._reconnect_attempts += 1
            error_msg = f"串口连接失败: {str(se)}"
            logger.critical(error_msg)
            self.serial_error.emit(error_msg)

            if self._reconnect_attempts > self._max_reconnect:
                critical_msg = f"连续 {self._max_reconnect} 次连接失败, 终止重试"
                logger.critical(critical_msg)
                self.serial_error.emit(critical_msg)
                self.running = False
        except Exception as e:
            logger.critical(f"串口连接过程中未知异常: {e}")
            self.serial_error.emit(f"未知连接错误: {str(e)}")
            self._reconnect_attempts += 1

        # 连接失败后的处理
        if self.running:
            wait_time = min(2 ** self._reconnect_attempts, 30)  # 指数退避
            logger.warning(f"{wait_time}秒后尝试重新连接")
            time.sleep(wait_time)

        return False

    def _idle_sleep(self):
        """在无工作状态时休眠"""
        with QMutexLocker(self._lock):
            if self.ser.in_waiting == 0 and not self._send_queue:
                # 使用条件变量等待唤醒或超时
                self._condition.wait(self._lock, 50)  # 最长等待50ms

    def _process_send_queue(self):
        """处理发送队列中的报文"""
        with QMutexLocker(self._lock):
            if not self._send_queue or not self.ser or not self.ser.is_open:
                return

            # 按优先级排序（immediate=True的优先）
            self._send_queue.sort(key=lambda x: not x[0])
            priority, data, expected_response_info = self._send_queue.pop(0)

            try:
                if not isinstance(data, bytes):
                    raise TypeError("发送数据必须为bytes类型")

                bytes_written = self.ser.write(data)
                self.ser.flush()

                if bytes_written == len(data):
                    self.send_complete.emit(True, data)

                    # 设置期望的响应信息和超时定时器
                    if expected_response_info:
                        self._current_expected_response = expected_response_info
                        self._start_response_timer()
                else:
                    error_msg = f"发送不完整: {bytes_written}/{len(data)}字节"
                    logger.warning(error_msg)
                    self.serial_error.emit(error_msg)
                    self.send_complete.emit(False, data)
            except SerialTimeoutException:
                error_msg = "发送超时: 可能串口已断开"
                logger.critical(error_msg)
                self.serial_error.emit(error_msg)
                self.send_complete.emit(False, data)
                self._close_serial()
            except SerialException as se:
                error_msg = f"串口发送错误: {str(se)}"
                logger.critical(error_msg)
                self.serial_error.emit(error_msg)
                self.send_complete.emit(False, data)
                self._close_serial()
            except Exception as e:
                logger.critical(f"发送过程中未知异常: {e}")
                self.serial_error.emit(f"未知发送错误: {str(e)}")
                self.send_complete.emit(False, data)

    # 响应超时处理函数
    def _start_response_timer(self):
        if self._response_timer is not None:
            self._response_timer.stop()

        self._response_timer = QTimer()
        self._response_timer.setSingleShot(True)
        self._response_timer.timeout.connect(self._on_response_timeout)
        self._response_timer.start(500)  # 500ms超时

    # 响应超时处理
    def _on_response_timeout(self):
        if self._current_expected_response:
            func_code, addr = self._current_expected_response
            logger.warning(f"MODBUS响应超时: 功能码 {func_code}, 地址 {addr}")
            self.modbus_response_timeout.emit(func_code, addr)
            self._current_expected_response = None

    # 接收并处理串口数据
    def _process_receive_data(self):
        try:
            if not self.ser or not self.ser.is_open:
                return

            in_waiting = self.ser.in_waiting
            if in_waiting > 0:
                # 批量读取（高效处理）
                data = self.ser.read(in_waiting)

                with QMutexLocker(self._lock):
                    if len(self._buffer) + len(data) > self._max_buffer_size:
                        overflow = (len(self._buffer) + len(data)) - self._max_buffer_size
                        logger.critical(f"接收缓冲区溢出! 丢弃最旧的{overflow}字节")
                        self._buffer = self._buffer[overflow:]

                    self._buffer.extend(data)
                    self._process_buffer()
        except SerialException as se:
            logger.critical(f"接收数据时串口错误: {str(se)}")
            self.serial_error.emit(f"接收错误: {str(se)}")
            self._close_serial()
        except Exception as e:
            logger.critical(f"接收数据时未知异常: {e}")
            self.serial_error.emit(f"未知接收错误: {str(e)}")

    def _process_buffer(self):
        """处理接收缓冲区"""
        packet_size = 245
        try:
            while len(self._buffer) >= packet_size:
                packet = bytes(self._buffer[:packet_size])
                self._buffer = self._buffer[packet_size:]

                # 在锁外发射信号
                if len(packet) > 0:
                    # 确保数据有效再发射信号
                    self._handle_modbus_response(packet)
        except Exception as e:
            logger.critical(f"处理缓冲区时发生异常: {e}")
            self.serial_error.emit(f"缓冲区错误: {str(e)}")

    # 处理MODBUS响应报文
    def _handle_modbus_response(self, packet):
        try:
            # 解析MODBUS报文
            if len(packet) < 3:
                self.data_received.emit(packet)
                return

            # 解析功能码
            func_code = packet[1] if len(packet) > 1 else 0

            # 根据当前期望的响应信息发射不同的信号
            if self._current_expected_response:
                expected_func_code, expected_address = self._current_expected_response

                # 根据期望的功能码和地址发射不同的信号
                if func_code == 0x03:  # 读保持寄存器响应
                    if expected_address == 0x05DD:  # 温度数据地址
                        self.modbus_read_temperature.emit(packet)
                    elif expected_address == 0x044D:  # 其他特定地址
                        self.modbus_read_device.emit(packet)
            else:
                # 没有期望的响应信息，使用通用信号
                self.data_received.emit(packet)

            # 停止超时定时器
            if self._response_timer:
                self._response_timer.stop()
                self._response_timer = None
                self._current_expected_response = None

        except Exception as e:
            logger.error(f"处理MODBUS响应时出错: {e}")
            self.data_received.emit(packet)  # 出错时仍发送通用信号

    # 发送数据
    def send_data(self, data, immediate=False, expected_response=None):
        """
        发送数据

        Args:
            data: 要发送的字节数据
            immediate: 是否立即发送
            expected_response: 期望的响应信息 (function_code, address)
        """
        try:
            if not isinstance(data, bytes):
                raise TypeError("发送数据必须为bytes类型")

            if not self.running:
                logger.critical("线程未运行，数据将被忽略")
                return False

            with QMutexLocker(self._lock):
                self._send_queue.append((immediate, data, expected_response))
                self._condition.wakeOne()  # 唤醒线程

            logger.debug(f"数据加入发送队列: {data.hex()}{' (优先)' if immediate else ''}")
            return True
        except Exception as e:
            logger.exception(f"发送请求失败: {e}")
            self.serial_error.emit(f"发送请求错误: {str(e)}")
            return False

    def _close_serial(self):
        try:
            if self.ser and self.ser.is_open:
                self.ser.close()
                self.serial_status.emit("已断开")
        except Exception as e:
            logger.exception(f"关闭串口时出错: {e}")
        finally:
            self.ser = None

    def cleanup(self):
        """停止运行并清理资源"""
        self.running = False

        # 唤醒所有等待的条件
        with QMutexLocker(self._lock):
            self._condition.wakeAll()

        # 关闭串口
        self._close_serial()

        # 清空缓冲区
        with QMutexLocker(self._lock):
            self._buffer.clear()
            self._send_queue.clear()

        # 停止定时器
        if self._response_timer:
            self._response_timer.stop()
            self._response_timer = None

        # 等待线程结束
        if not self.wait(3000):  # 最多等待3秒
            logger.critical("线程未能在超时时间内停止，将强制终止")
            self.terminate()


    def stop(self):
        """停止线程的公共接口"""
        self.cleanup()
